#pragma once
#include <iostream>
#include <functional>

#include <cerrno>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <wait.h>
#include <pthread.h>

#include "Log.hpp"
#include "sockaddr.hpp"
#include "Comm.hpp"
#include "nocopy.hpp"
#include "ProtectResourse.hpp"
#include "threadpool.hpp"
#include "function.hpp"

#define DEFAULT_LISTEN_SOCKFD -1
#define DEFAULT_BACKLOG_SIZE 5
#define BUFFERSIZE 1024

using task_t = std::function<void()>;

class Server : public nocopy // 防止拷贝
{
private:
    void Receive(int sockfd) // static也是为了多线程加的
    {
        // 提供给Client功能选择
        std::string fun = Choose_Function(sockfd);
        // 这里是获取连接成功， 开始接收信息
        char read_buffer[BUFFERSIZE];
        // 接收数据
        ssize_t ret_read = read(sockfd, read_buffer, sizeof(read_buffer) - 1);
        // 读错误
        if (ret_read < 0)
        {
            CheckRet(ret_read, server_name, func_types[4]);
            exit(1);
        }
        // 读到数据
        else
        {
            read_buffer[ret_read] = 0;
            std::cout << "[" << fun << "]# " << read_buffer << std::endl;
            std::string rebuffer;
            rebuffer += "[";
            rebuffer += fun;
            rebuffer += "]# ";
            if (fun == "translate") rebuffer += Translate(read_buffer);
            else if (fun == "transform") rebuffer += Transform(read_buffer);
            else if (fun == "ping") rebuffer += Ping();
            ssize_t ret_write = write(sockfd, rebuffer.c_str(), rebuffer.size());
            CheckRet(ret_write, server_name, func_types[5]);
        }

        close(sockfd);
    }

    std::string Choose_Function(int sockfd)
    {
        std::string write_buffer = "请选择功能#";
        for (auto &e : func_name)
        {
            write_buffer += " [";
            write_buffer += e;
            write_buffer += "] ";
        }
        int ret_write = write(sockfd, write_buffer.c_str(), write_buffer.size());
        CheckRet(ret_write, server_name, func_types[5]);

        char read_buffer[128];
        int ret_read = read(sockfd, read_buffer, sizeof(read_buffer));
        CheckRet(ret_read, server_name, func_types[4]);
        read_buffer[ret_read] = 0;
        std::cout << "client 选择了" << read_buffer << std::endl;
        return read_buffer;
    }

public:
    Server(uint16_t port)
        : _listen_sockfd(DEFAULT_LISTEN_SOCKFD), _sk(port)
    {
    }
    ~Server()
    {
    }
    // 初始化Server端
    void Init()
    {
        // 获取listen套接字，此套接字用于监听client的请求连接
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        // 检查返回值
        CheckRet(_listen_sockfd, server_name, func_types[0]);

        // 解决server主动关闭，无法重启，bind失败的问题
        int opt = 1;
        setsockopt(_listen_sockfd,
                   SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                   &opt, sizeof(opt));

        // bind
        int ret_bind = ::bind(_listen_sockfd, (const sockaddr *)(&_sk.addr_in()), _sk.len());
        // 检查返回值
        CheckRet(ret_bind, server_name, func_types[1]);

        // 这里表示监听client,监听到一个client，将其入队列，再由accept一个一个拿
        int ret_listen = listen(_listen_sockfd, DEFAULT_BACKLOG_SIZE);
        // 检查返回值
        CheckRet(ret_listen, server_name, func_types[2]);
    }

    // server开始服务
    void Start()
    {
        // 【v4：线程池】
        ThreadPool<task_t>::GetOnce()->Detach_Threads(); // 分离线程池的线程
        ThreadPool<task_t>::GetOnce()->Start_Task();     // 让线程池的线程执行任务

        // 使用循环的原因：server可能会获取多个连接，同时有多个client要通信
        // 所以我们就要挨个建立连接（但这里只是单进程，只能获取一个client的连接）
        while (true)
        {
            // 输出型参数，获取client的sockaddr配置信息
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 建立连接！
            int sockfd = accept(_listen_sockfd, (sockaddr *)(&peer), &len); // 这里不需要进行错误排查，没必要，没有收到连接就阻塞
            // 获取连接成功
            CheckRet(0, server_name, func_types[3]);
            //[线程池版本]
            ThreadPool<task_t>::GetOnce()->Push(std::bind(&Server::Receive, this, sockfd)); // 向线程池派发任务
        }
    }

private:
    int _listen_sockfd;
    Sockaddr _sk;
};